//Copyright 2008 Kristian Gundry And Rune Juhl-Petersen
//This file is part of Plain.

//Plain is free software: you can redistribute it and/or modify
//it under the terms of the GNU Lesser General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//any later version.

//Plain is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU Lesser General Public License for more details.

//You should have received a copy of the GNU Lesser General Public License
//along with Plain.  If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Web;

using NHibernate;
using NHibernate.Cfg;
using System.Collections;

namespace NHibernate.Helper
{
	/// <summary> 
	///        Provides access to a single NHibernate session on a per request basis. 
	///		   From: http://blogs.intesoft.net/simon/articles/16.aspx
	/// </summary> 
	/// <remarks> 
	///        NHibernate requires mapping files to be loaded. These can be stored as 
	///        embedded resources in the assemblies alongside the classes that they are for or 
	///        in separate XML files. 
	///        <br /><br /> 
	///        As the number of assemblies containing mapping resources may change and 
	///        these have to be referenced by the NHibernate config before the SessionFactory 
	///        is created, the configuration is stored in a separate config file to allow 
	///        new references to be added. This would also enable the use of external mapping 
	///        files if required. 
	/// </remarks> 

	public sealed class Broker
	{
		/// <summary> 
		/// Key used to identify NHibernate session in context items collection 
		/// </summary> 
		private const string sessionKey = "NHibernate.Broker";

		/// <summary> 
		/// NHibernate Configuration 
		/// </summary> 
		private static readonly Configuration configuration = new Configuration();

		/// <summary> 
		/// NHibernate SessionFactory 
		/// </summary> 
		private static readonly ISessionFactory sessionFactory = configuration.Configure().BuildSessionFactory();

		/// <summary> 
		/// NHibernate Session. This is only used for NUnit testing (ie. when HttpContext 
		/// is not available. 
		/// </summary> 
		private static readonly ISession session = sessionFactory.OpenSession();

		/// <summary> 
		/// None public constructor. Prevent direct creation of this object.  
		/// </summary> 
		private Broker() { }

		/// <summary> 
		/// See beforefieldinit 
		/// </summary> 
		static Broker() { }

		/// <summary> 
		/// Creates a new NHibernate Session object if one does not already exist. 
		/// When running in the context of a web application the session object is 
		/// stored in HttpContext items and has 'per request' lifetime. For client apps 
		/// and testing with NUnit a normal singleton is used. 
		/// </summary> 
		/// <returns>NHibernate Session object.</returns>
		public static ISession Session
		{
			get
			{
				ISession session;
				if (HttpContext.Current == null)
				{
					session = Broker.session;
				}
				else
				{
					if (HttpContext.Current.Items.Contains(sessionKey))
					{
						session = (ISession)HttpContext.Current.Items[sessionKey];
					}
					else
					{
						session = Broker.sessionFactory.OpenSession();
						HttpContext.Current.Items[sessionKey] = session;
					}
				}
				return session;
			}
		}

		/// <summary> 
		/// Closes any open NHibernate session if one has been used in this request.<br /> 
		/// This is called from the EndRequest event. 
		/// </summary>
		public static void CloseSession()
		{
			if (HttpContext.Current == null)
			{
				Broker.session.Close();
			}
			else
			{
				if (HttpContext.Current.Items.Contains(sessionKey))
				{
					ISession session = (ISession)HttpContext.Current.Items[sessionKey];
					session.Close();
					HttpContext.Current.Items.Remove(sessionKey);
				}
			}

		}

		/// <summary> 
		/// Returns the NHibernate Configuration object. 
		/// </summary> 
		/// <returns>NHibernate Configuration object.</returns> 
		public static NHibernate.Cfg.Configuration Configuration
		{
			get { return Broker.configuration; }
		}

		/// <summary> 
		/// Returns the NHibernate SessionFactory object. 
		/// </summary> 
		/// <returns>NHibernate SessionFactory object.</returns> 
		public static ISessionFactory SessionFactory
		{
			get { return Broker.sessionFactory; }
		}

		/// <summary> 
		/// Loads the specified object. 
		/// </summary> 
		/// <param name="id">Id.</param> 
		public static void Load<T>(object id)
		{
			Broker.Session.Load<T>(id);
		}

		/// <summary> 
		/// Gets the specified object. 
		/// </summary> 
		/// <param name="id">Id.</param> 
		public static T Get<T>(object id)
		{
			return Broker.Session.Get<T>(id);
		}

		/// <summary> 
		/// Gets the specified object. 
		/// </summary> 
		/// <param name="id">Id.</param> 
		public static object Get(System.Type objectType, object id)
		{
			return Broker.Session.Get(objectType, id);
		}


		/// <summary> 
		/// Save object item using NHibernate. 
		/// </summary> 
		/// <param name="item">Object to save</param> 
		public static void Save(object item)
		{
			ITransaction transaction = Broker.Session.BeginTransaction();
			try
			{
				Broker.Session.Save(item);
				transaction.Commit();
			}
			catch
			{
				transaction.Rollback();
				throw;
			}
		}

		/// <summary> 
		/// Save object item using NHibernate. 
		/// </summary> 
		/// <param name="item">Object to delete</param> 
		public static void Delete(object item)
		{
			ITransaction transaction = Broker.Session.BeginTransaction();

			try
			{
				Broker.Session.Delete(item);
				transaction.Commit();
			}
			catch
			{
				transaction.Rollback();
				throw;
			}
		}

		/// <summary>
		/// Retrieves a single scalar value
		/// </summary>
		/// <param name="query"></param>
		/// <param name="args"></param>
		/// <returns></returns>
		public static object GetScalar(string query, params object[] args)
		{
			if (args != null)
			{
				query = string.Format(query, args);
			}
			ISession s = Broker.Session;
			IList result = s.CreateQuery(query).List();
			//IList result = s.Find(query);
			if (result.Count != 1)
			{
				throw new ApplicationException("The query returned " + result.Count + " results. A scalar should only return one.");
			}
			return result[0];
		}


		/// <summary> 
		/// Creates the specified database. 
		/// </summary>
		public static void CreateDatabase()
		{
			NHibernate.Tool.hbm2ddl.SchemaExport se = new NHibernate.Tool.hbm2ddl.SchemaExport(Broker.Configuration);
			se.Create(true, true);
		}

	}
}


